using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using Meebey.SmartIrc4net;
using Thunder.Irc.Formatter.Default;

namespace Thunder.Irc.Formatter
{
    /// <summary>
    /// Manager for formatting message
    /// </summary>
    public sealed class FormatManager
    {
        private readonly Dictionary<ReceiveType, List<IMessageFormatter>> customFormatters = new Dictionary<ReceiveType, List<IMessageFormatter>>();
        private readonly Dictionary<ReceiveType, List<IMessageFormatter>> defaultFormatters = new Dictionary<ReceiveType, List<IMessageFormatter>>();

        private readonly List<IGenericFormatter> genericFormatters = new List<IGenericFormatter>();

        /// <summary>
        /// Gets the current <see cref="IrcClient"/> instance
        /// </summary>
        public IrcClient IrcClient { get; private set; }

        /// <summary>
        /// Constructor
        /// </summary>
        public FormatManager(IrcClient client)
        {
            this.IrcClient = client;
        }

        /// <summary>
        /// Formats the message using either default or custom message formatters
        /// </summary>
        /// <param name="data">The <see cref="IrcMessageData"/> to be with the original message data</param>
        /// <param name="message">The message to be formatted</param>
        public string FormatMessage(IrcMessageData data, string message)
        {
            CancelMessageFormattingEventArgs args = new CancelMessageFormattingEventArgs(data, message);
            if (this.customFormatters.ContainsKey(data.Type) == true)
            {
                foreach (IMessageFormatter customFormatter in this.customFormatters[data.Type])
                {
                    PermissionSet permissionSet = CreatePermissionInstance();
                    permissionSet.PermitOnly();
                    
                    customFormatter.Format(args);
                    if (args.Cancel == true)
                    {
                        break;
                    }
                }
            }

            if (args.Cancel == true)
            {
                return args.Message;
            }

            if (this.defaultFormatters.ContainsKey(data.Type) == true)
            {
                foreach (IMessageFormatter defaultParser in this.defaultFormatters[data.Type])
                {
                    defaultParser.Format(args);
                }
            }

            foreach (IGenericFormatter genericFormatter in this.genericFormatters)
            {
                MessageFormattingEventArgs messageArgs = new MessageFormattingEventArgs(args.MessageData, args.Message);
                genericFormatter.Format(messageArgs);

                args.Message = messageArgs.Message;
            }

            return args.Message;
        }

        /// <summary>
        /// Initializes the <see cref="FormatManager"/>, and loads all available parsers
        /// </summary>
        public void Initialize()
        {
            this.LoadCustomFormatters();
            this.LoadDefaultFormatters();

            this.LoadGenericFormatters();
        }

        /// <summary>
        /// Loads the custom <see cref="IMessageFormatter"/>'s for the parser directory
        /// </summary>
        private void LoadCustomFormatters()
        {
            if (Directory.Exists("Formatters") == false)
            {
                return;
            }

            string parsersPath = Environment.CurrentDirectory + "\\Formatters";
            string[] files = Directory.GetFiles(parsersPath, "*.dll");

            foreach (string file in files)
            {
                Assembly assembly;
                try
                {
                    assembly = Assembly.LoadFile(file);
                }
                catch (BadImageFormatException)
                {
                    continue;
                }

                PermissionSet permissionSet = CreatePermissionInstance();
                permissionSet.PermitOnly();
                
                this.LoadFormatters(assembly, this.customFormatters);
            }
        }

        /// <summary>
        /// Loads the list of default <see cref="IMessageFormatter"/>'s
        /// </summary>
        private void LoadDefaultFormatters()
        {
            this.LoadFormatters(Assembly.GetAssembly(this.GetType()), this.defaultFormatters);
        }

        /// <summary>
        /// Loads all available parsers from the provided assembly into the provided parser dictionary
        /// </summary>
        /// <param name="assembly">The <see cref="Assembly"/> to load the parsers from</param>
        /// <param name="parsers">The list of parsers to load the parser into</param>
        private void LoadFormatters(Assembly assembly, IDictionary<ReceiveType, List<IMessageFormatter>> parsers)
        {
            Type[] types = assembly.GetTypes();

            foreach (Type type in types)
            {
                if (type.IsSubclassOf(typeof (MessageFormatter)) == false)
                {
                    continue;
                }

                IMessageFormatter formatter = Activator.CreateInstance(type) as IMessageFormatter;
                if (formatter == null)
                {
                    continue;
                }

                formatter.Initialize(this);
                if (parsers.ContainsKey(formatter.MessageType) == false)
                {
                    parsers.Add(formatter.MessageType, new List<IMessageFormatter>());
                }

                parsers[formatter.MessageType].Add(formatter);
            }
        }

        /// <summary>
        /// Creates the permitted permissions for the custom formatters
        /// </summary>
        /// <returns></returns>
        private static PermissionSet CreatePermissionInstance()
        {
            PermissionSet permissionSet = new PermissionSet(PermissionState.None);

            return permissionSet;
        }

        /// <summary>
        /// Loads and initializes the generic <see cref="IMessageFormatter"/>s
        /// </summary>
        private void LoadGenericFormatters()
        {
            Type[] types = Assembly.GetAssembly(this.GetType()).GetTypes();

            foreach (Type type in types)
            {
                if (type.IsSubclassOf(typeof (GenericFormatter)) == false)
                {
                    continue;
                }

                IGenericFormatter formatter = Activator.CreateInstance(type) as IGenericFormatter;
                if (formatter == null)
                {
                    continue;
                }

                formatter.Initialize(this);

                this.genericFormatters.Add(formatter);
            }

            return;
        }

        public string GetImagePath(IrcMessageData data)
        {
            if (this.customFormatters.ContainsKey(data.Type) == true)
            {
                foreach (IMessageFormatter messageFormatter in customFormatters[data.Type])
                {
                    if (string.IsNullOrEmpty(messageFormatter.ImagePath) == true)
                    {
                        continue;
                    }

                    return messageFormatter.ImagePath;
                }
            }

            if (this.defaultFormatters.ContainsKey(data.Type) == true)
            {
                foreach (IMessageFormatter messageFormatter in defaultFormatters[data.Type])
                {
                    if (string.IsNullOrEmpty(messageFormatter.ImagePath) == true)
                    {
                        continue;
                    }

                    return messageFormatter.ImagePath;
                }
            }

            return string.Empty;
        }
    }
}